#include "sync_alg.hpp"
#include <cstring>

sync_alg::sync_alg(int device_num_,int buffer_num_)
  : device_num(device_num_)
  , buffer_num(buffer_num_)
  , ResultList(device_num_)
  , threadID(pthread_self())
{
  //最开始这两个指针应该是同时指向开始位置
  insertPos=storeDataList.begin();
  //准备一个默认桶
  itrDataQueue.insert(
    std::pair<unsigned long,bottleItemType>(
      0,
      {
        std::vector<std::list<BigImageType>::iterator>(0),
        0,
        0
      }
    ));
  itrBottleBegin=itrDataQueue.begin();
}

sync_alg::~sync_alg()
{
  if(pthread_equal(threadID,pthread_self())==0)
    pthread_cancel(threadID);
  
}

void sync_alg::Boot()
{
  //启动算法线程
  workThread=std::jthread(&sync_alg::workFunction,this);
}

void sync_alg::setResultCallback(std::function<void(const std::vector<ImageType>&)> func)
{
  funcResultCallback=func;
}

void sync_alg::InputImage(ImageType image,int camera_index)
{
  {
    std::lock_guard<std::mutex> lck(mutStoreDataList);
    storeDataList.push_back({image,camera_index});
    auto ptr=std::prev(storeDataList.end());
    strcpy(ptr->data.uuid,image.uuid);
    if(insertPos==storeDataList.end())
      insertPos=ptr;
  }
  cvNewData.notify_one();
}

void sync_alg::workFunction(std::stop_token stoken)
{
  threadID=pthread_self();
  //辅助函数，用来回收桶内的数据指针
  auto RecoveryBottleItem=[&](bottleType::iterator itr){
    for(int i=0;i<itr->second.dataSize;i++)
    {
      //删除数据指针
      delete [] itr->second.dataRef.at(i)->data.data;
      //在DataList中删除这个指针
      storeDataList.erase(itr->second.dataRef.at(i));
    }
  };
  while(true)
  {
    std::unique_lock<std::mutex> lck(mutStoreDataList);
    cvNewData.wait(lck,[this](){return insertPos!=storeDataList.end();});
    if(stoken.stop_requested()) return;
    if(storeDataList.size()>=50 || itrDataQueue.size()>=50)
      
    //处理新数据
    for(;insertPos!=storeDataList.end();)
    {
      bottleType::iterator bottleItr=itrBottleBegin;
      bool loopRun=false;
      do
      {
        loopRun=false;
        auto bottleItr=itrDataQueue.find(insertPos->data.FrameID);
        if(bottleItr==itrDataQueue.end())
          //找不到对应在帧号
        {
          if(std::prev(itrDataQueue.end())->first<insertPos->data.FrameID)
          //说明桶用完了，要分配新的桶
          {
            if(itrDataQueue.size()>=buffer_num)
            //缓冲区用完了，准备释放内存
            {
              //检查是否有可以回收的桶
              if(itrBottleBegin!=itrDataQueue.begin())
              //说明有垃圾可以回收
              {
                
                for(auto queue_ptr=itrDataQueue.begin();queue_ptr!=itrBottleBegin;)
                {
                  //先回收桶内元素
                  RecoveryBottleItem(queue_ptr);
                  //然后删除桶
                  auto deleteQueue=queue_ptr;
                  queue_ptr++;
                  itrDataQueue.erase(deleteQueue);
                }
              }
              else
              //没有垃圾进行回收，就释放第一个桶
              {
                auto deletePos=itrBottleBegin;
                itrBottleBegin++;
                RecoveryBottleItem(deletePos);
                itrDataQueue.erase(deletePos);
              }
            }
            //分配一个新的桶
            itrDataQueue.insert(
              std::pair<unsigned long,bottleItemType>
              (
                insertPos->data.FrameID,
                {
                  std::vector<std::list<BigImageType>::iterator>(device_num),
                  insertPos->data.FrameID,
                  0
                }
              )
            );
            loopRun=true;
          }
          else
          //说明帧号太小了
          {
            auto deletePtr=insertPos;
            insertPos++;
            //删除指针
            delete [] deletePtr->data.data;
            storeDataList.erase(deletePtr);
          }
        }
        else
        {
          //将新数据添加到桶中
          bottleItr->second.dataRef[bottleItr->second.dataSize]=insertPos;
          //将数据处理位置+1
          bottleItr->second.dataSize++;
          //说明桶满了
          if(bottleItr->second.dataSize==device_num)
          {
            //把这个桶中的数据倒入结果中
            for(int i=0;i<device_num;i++)
            {
              ResultList.at(i)=bottleItr->second.dataRef.at(i)->data;
              strcpy(ResultList.at(i).uuid,bottleItr->second.dataRef.at(i)->data.uuid);
            }
            //将结果发送出去
            funcResultCallback(ResultList);
            //将桶指针往后一格
            itrBottleBegin=(bottleItr++);
          }
          insertPos++;
        }
      }
      while(loopRun);
    }
  }
}

